package cn.hicard.core;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * 生成实例“数据模型”的查询SQL或HQL条件。
 * 实体的属性严格遵循以<b><font color='red'>小写开头</font></b>。
 * 使用时，T的实例Entity的getter方法和settter方法一定要规范，
 * 作为条件添加的属性的getter和setter对应的字段一定要在数据库中存在。<br/>
 * 为了较好的性能，这里只做一般性检查，不做严格检查
 * @author zhangy
 */
public class Where {
		
	// 泛型的具体类的所有公共getter的名称，即所有公共可访问字段
	private List<String> fields = new ArrayList<String>();
	// 当前实例的Class
	private Class clss;
	
	// 未左右闭合的括号数量
	private int unsealedBracketCount = 0;
	
	/** 连接符'AND' */
	private final String JOINER_AND = " AND ";
	/** 连接符'OR' */
	private final String JOINER_OR = " OR ";
	
	/** 当前的条件连接符AND或者OR，默认为AND */
	private String joiner = JOINER_AND;
	/** 是否检查字段 */
	private boolean checkField = true;
	/** 是否忽略空值或空字符串，默认为true */
	private boolean ignoreNullAndEmpty = true;
	
	/** 用于拼装的查询字符串 */
	private StringBuffer ql  = new StringBuffer(" ");
	/** 查询字符串中的参数，它用于代替问号(?)表示的占位符 */
	private List<Object> params = new ArrayList<Object>();
	
	/**
	 * 通过实例的Class构造，默认检查字段，进行HQL查询时推荐使用
	 * @param clz
	 */
	public Where(Class clz){
		clss = clz;
		// 取得所有getter和setter，当两者都存在时则认为这是一个合法字段
		// 但是考虑到效率还是让使用者自己规范，这里就不做过多的检查了。
		Method[] mds = clz.getMethods();		
		for (Method md : mds) {
			// 取得符合"getter和setter"的方法名
			String mdName = md.getName();	
			if(mdName.startsWith("get")){
				fields.add(mdName.substring(3, 4).toLowerCase() + mdName.substring(4));
			} 
		}
	}
	
	
	/**
	 * 通过实例的Class和工作模式构造，默认检查字段，进行HQL查询时推荐使用
	 * @param clz
	 */
	public Where(Class clz, int mode){
		clss = clz;
		setMode(mode);
		// 取得所有getter和setter，当两者都存在时则认为这是一个合法字段
		// 但是考虑到效率还是让使用者自己规范，这里就不做过多的检查了。
		Method[] mds = clz.getMethods();		
		for (Method md : mds) {
			// 取得符合"getter和setter"的方法名
			String mdName = md.getName();	
			if(mdName.startsWith("get")){
				fields.add(mdName.substring(3, 4).toLowerCase() + mdName.substring(4));
			} 
		}
	}
	
	/**
	 * 取得条件部分的查询语句
	 * @return
	 */
	public String queryString(){
		if(unsealedBracketCount != 0){
			throw new WhereException("存在" + unsealedBracketCount + "个未封闭的括号");
		}
		return ql.toString().trim();
	}
	
	/**
	 * 取得参数的List
	 * @return
	 */
	public List<Object> getParamList(){
		return params;
	}
	
	/**
	 * 取得参数的数组
	 * @return
	 */
	public Object[] getParams(){
		return params.toArray();
	}
	
	/**
	 * 检查属性字段是否存在于当前实例类型中
	 * @param field
	 */
	private void check(String field){
		if(checkField && fields.contains(field) == false){
			throw new WhereException("field is not exists. 当前实例类型" + clss.getName() + "中不存在字段[" + field + "]");
		}
	}
	
	/**
	 * 判断是否忽略空值或空字符串，true不忽略，false忽略。
	 * @return
	 */
	private boolean notIgnore(Object value){
		if(ignoreNullAndEmpty){
			return value != null && value.toString().length()>0;
		}else{
			return true;
		}
	}
	
	/**
	 * 设定工作模式
	 * @param mode
	 */
	public void setMode(int mode){
		switch (mode) {
		case MODE_JOINER_AND:
			joiner = JOINER_AND;
			break;
		case MODE_JOINER_OR:
			joiner = JOINER_OR;
			break;
		case MODE_CHECK:
			checkField = true;
			break;
		case MODE_NOT_CHECK:
			checkField = false;
			break;
		case MODE_IGNORE:
			ignoreNullAndEmpty = true;
			break;
		case MODE_NOT_IGNORE:
			ignoreNullAndEmpty = false;
			break;
		default:
			break;
		}
	}
	
	/**
	 * 确认当前是否为HQL查询，true为HQL查询，false为SQL查询。<BR/>
	 * HQL查询会检查字段是否存在；SQL则不会检查，也不无法检查。
	 * @return
	 */
	public boolean isHQL(){
		return checkField;
	}
	
	/**
	 * 括号开始
	 */
	public void startBracket(){
		ql.append(joiner + " (");
		unsealedBracketCount++;
	}

	/**
	 * 括号结束
	 */
	public void endBracket(){
		ql.append(") ");
		unsealedBracketCount--;
	}
	
	/**
	 * 追加连接符
	 */
	public void appendJoiner(){
		if(ql.charAt(ql.length()-1) != '('){
			ql.append(joiner);	
		}
	}
	
	/**
	 * 添加“等于”
	 * @param field
	 * @param value
	 */
	public void addEqual(String field, Object value){
		check(field);
		if(notIgnore(value)){
			appendJoiner();
			ql.append(field + "=?");
			params.add(value);
		}
	}
	
	/**
	 * 添加“不等于”
	 * @param field
	 * @param value
	 */
	public void addNotEqual(String field, Object value){
		check(field);
		if(notIgnore(value)){
			appendJoiner();
			ql.append(field + "!=?");
			params.add(value);
		}
	}
	
	/**
	 * 添加“大于”
	 * @param field
	 * @param value
	 */
	public void addGreater(String field, Object value){
		check(field);
		if(notIgnore(value)){
			appendJoiner();
			ql.append(field + ">?");
			params.add(value);
		}
	}
	
	/**
	 * 添加“大于等于”
	 * @param field
	 * @param value
	 */
	public void addGreaterEq(String field, Object value){
		check(field);
		if(notIgnore(value)){
			appendJoiner();
			ql.append(field + ">=?");
			params.add(value);
		}
	}
	
	/**
	 * 添加“小于”
	 * @param field
	 * @param value
	 */
	public void addLess(String field, Object value){
		check(field);
		if(notIgnore(value)){
			appendJoiner();
			ql.append(field + "<?");
			params.add(value);
		}
	}
	
	/**
	 * 添加“小于等于”
	 * @param field
	 * @param value
	 */
	public void addLessEq(String field, Object value){
		check(field);
		if(notIgnore(value)){
			appendJoiner();
			ql.append(field + "<=?");
			params.add(value);
		}
	}
	
	/**
	 * 添加“模糊匹配like”的最常用型[%value%]
	 * @param field
	 * @param value
	 */
	public void addLike(String field, String value){
		check(field);
		if(notIgnore(value)){
			appendJoiner();
			ql.append(field + " LIKE ?");
			params.add("%" + value + "%");
		}
	}
	
	/**
	 * 添加“模糊匹配like”，使用自定义的pattern匹配，value仅用于空值校验
	 * @param field
	 * @param value
	 * @param pattern
	 */
	public void addLike(String field, String value, String pattern){
		check(field);
		if(notIgnore(value)){
			appendJoiner();
			ql.append(field + " LIKE ?");
			params.add(pattern);
		}
	}
	
	/**
	 * 添加“区间类型条件”
	 * @param field
	 * @param startEq是否包含起始值
	 * @param start
	 * @param endEq是否包含结束值
	 * @param end
	 */
	public void addBetween(String field, boolean startEq, Object start, boolean endEq, Object end){
		check(field);
		if(notIgnore(start)){
			if(startEq){
				appendJoiner();
				ql.append(field +  ">=?");
				params.add(start);
			}else{
				appendJoiner();
				ql.append(field + ">?");
				params.add(start);
			}			
		}
		if(notIgnore(end)){
			if(endEq){
				appendJoiner();
				ql.append(field + "<=?");
				params.add(end);
			}else{
				appendJoiner();
				ql.append(field + "<?");
				params.add(end);
			}			
		}
	}
	
	/**
	 * 添加“区间类型条件，大于等于 ‘且’ 小于等于，即 field>=? AND field<=?”
	 * @param field
	 * @param start
	 * @param end
	 */
	public void addBetween(String field, Object start, Object end){
		addBetween(field, true, start, true, end);
	}
	
	/**
	 * 添加“IN查询条件”
	 * @param field
	 * @param items
	 */
	public void addIn(String field, Object... objs){
		check(field);
		List<Object> items = Arrays.asList(objs);
		if(items == null || items.size()==0){
			return;
		}
		// 为了支持remove操作
		items = new ArrayList<Object>(items);
		// 空值忽略
		Iterator<Object> it = items.iterator();
		while(it.hasNext()){
			Object item = it.next();
			if(notIgnore(item) == false){
				it.remove();
			}
		}
		
		if(items.size()>0){
			appendJoiner();
			ql.append(field + " IN (");
			for (Object item : items) {
				ql.append("?,");
				params.add(item);
			}
			ql.deleteCharAt(ql.length() - 1);
			ql.append(")");
		}		
	}
	
	/**
	 * 添加“NOT IN查询条件”
	 * @param field
	 * @param items
	 */
	public void addNotIn(String field, Object... objs){
		check(field);
		List<Object> items = Arrays.asList(objs);
		if(items == null || items.size()==0){
			return;
		}
		// 为了支持remove操作
		items = new ArrayList<Object>(items);
		// 空值忽略
		Iterator<Object> it = items.iterator();
		while(it.hasNext()){
			Object item = it.next();
			if(notIgnore(item) == false){
				it.remove();
			}
		}
		
		if(items.size()>0){
			appendJoiner();
			ql.append(field + " NOT IN (");
			for (Object item : items) {
				ql.append("?,");
				params.add(item);
			}
			ql.deleteCharAt(ql.length() - 1);
			ql.append(")");
		}		
	}
	
	
	
	

	/** 模式:查询字符串连接符为AND  */
	public final static int MODE_JOINER_AND = 1;
	/** 模式:查询字符串连接符为OR */
	public final static int MODE_JOINER_OR = 2;
	/** 模式:检查字段，并转换为HQL查询 */
	public final static int MODE_CHECK = 3;
	/** 模式:不检查字段，并转换为SQL查询 */
	public final static int MODE_NOT_CHECK = 4;
	/** 模式:忽略NULL值或空字符串 */
	public final static int MODE_IGNORE = 5;
	/** 模式:不忽略NULL值或空字符串 */
	public final static int MODE_NOT_IGNORE = 6;
}
